import torch
import torch.nn.functional as F
import matplotlib.pyplot as plt


class Net(torch.nn.Module):
    def __init__(self, n_feature, n_hidden, n_output):
        super(Net, self).__init__()  # 调用父类的构造函数
        self.hidden = torch.nn.Linear(n_feature, n_hidden)  # hidden layer
        self.predict = torch.nn.Linear(n_hidden, n_output)  # output layer

    def forward(self, x):
        x = F.relu(self.hidden(x))  # activation function for hidden layer
        x = self.predict(x)  # linear output
        return x


x = torch.unsqueeze(torch.linspace(-1, 1, 100), dim=1)  # x data(tensor),shape=(100,1)
y = x.pow(2) + 0.2 * torch.rand(x.size())  # add noisy y data(tensor),shape=(100,1)
net = Net(n_feature=1, n_hidden=10, n_output=1)  # 输入特征为1，隐藏层10个神经元，输出单变量
# print(net)  # net architecture

optimizer = torch.optim.SGD(net.parameters(), lr=0.2)  # 传入net的所有参数，学习率
loss_func = torch.nn.MSELoss()  # this is for regression mean squared loss

plt.ion()  # 打开交互模式

for t in range(400):
    prediction = net(x)  # input x and predict based on x
    loss = loss_func(prediction, y)  # must be (1. nn output, 2. target)
    optimizer.zero_grad()  # clear gradients for next train
    loss.backward()  # backpropagation, compute gradients
    optimizer.step()  # apply gradients

    if t % 5 == 0:
        # plot and show learning process
        plt.cla()
        plt.scatter(x.data.numpy(), y.data.numpy())
        plt.plot(x.data.numpy(), prediction.data.numpy(), 'r-', lw=5)  # 画出拟合的曲线，红色，线宽5
        plt.text(0, 1, 'iter=%d, Loss=%.4f' % (t, loss.data.numpy()), fontdict={'size': 10, 'color': 'red'})
        plt.pause(0.1)

plt.ioff()
plt.show()

# 保存模型
PATH = './second_example_net.pth'
torch.save(net.state_dict(), PATH)

# 重新加载保存的模型
new_net = Net(n_feature=1, n_hidden=10, n_output=1)
new_net.load_state_dict(torch.load(PATH))
prediction = new_net(x)
loss = loss_func(prediction, y)
print(loss.data.numpy())
